package com.yycx.base.provider.config;


import com.yycx.base.provider.exception.NoResourcesException;
import com.yycx.base.provider.service.UserDetailsServiceImpl;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.common.constants.CommonConstants;
import com.yycx.common.security.OpenUser;
import com.yycx.common.security.passwordencoder.Md5PasswordEncoder;
import com.yycx.common.utils.RedisUtils;
import com.yycx.common.utils.WebUtils;
import com.yycx.module.admin.client.entity.BaseAccount;
import com.yycx.module.admin.provider.service.BaseAccountService;
import com.yycx.module.system.client.entity.SysCompany;
import com.yycx.module.system.provider.service.SysCompanyService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 实现自己的AuthenticationProvider类，用来自定义用户校验机制
 *
 * @author zyf
 * @date 2018/9/5
 */
@Component
public class AdminAuthenticationProvider implements AuthenticationProvider {

    @Resource
    private UserDetailsServiceImpl userDetailsService;
    @Resource
    private BaseAccountService baseAccountService;
    @Resource
    private SysCompanyService companyService;
    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private RedisUtils redisUtils;



    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取表单输入中返回的用户名;
        String accountName = (String) authentication.getPrincipal();
        // 获取表单中输入的密码；
        String password = (String) authentication.getCredentials();
        String companyId = WebUtils.getHttpServletRequest().getParameter("companyId");
        // 查询用户
        OpenUser userInfo = (OpenUser) userDetailsService.loadUserByUsername(accountName);
        if (userInfo == null) {
            throw new BadCredentialsException("用户名不存在");
        }
        // 这里还可以加一些其他信息的判断，比如用户账号已停用等判断。
        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        if(FlymeUtils.isEmpty(authorities)){
            throw new NoResourcesException();
        }
        //密码输错次数
        String errorCountKey = CommonConstants.ACCOUNT_LOCK_ERROR_COUNT + accountName;
        //锁定用户KEY
        String errorKey = CommonConstants.ACCOUNT_LOCK_ERROR_KEY + accountName;

        //检测用户是否锁定
        boolean hasErrorKey = redisUtils.hasKey(errorKey);
        if (FlymeUtils.isNotEmpty(hasErrorKey) && hasErrorKey == true) {
            throw new LockedException("User account is locked");
        }
        //当前错误次数
        Integer errorCount = redisUtils.getInteger(errorCountKey, 0);
        // 这里我们还要判断密码是否正确，这里我们的密码使用PasswordEncoder进行加密的
        if(passwordEncoder instanceof Md5PasswordEncoder){
            Long accountId = userInfo.getAccountId();
            BaseAccount baseAccount = baseAccountService.getById(accountId);
            Md5PasswordEncoder md5PasswordEncoder=(Md5PasswordEncoder)passwordEncoder;
            md5PasswordEncoder.setAccount(accountName);
            md5PasswordEncoder.setSalt(baseAccount.getMd5Salt());
        }
        if (!passwordEncoder.matches(password, userInfo.getPassword())) {
            errorCount += 1;
            //记录错误次数
            redisUtils.set(errorCountKey, errorCount, CommonConstants.ACCOUNT_LOCK_ERROR_CHECK_TIME);
            if (errorCount >= CommonConstants.ACCOUNT_LOCK_ERROR_MAX_COUNT) {
                //锁定3小时
                redisUtils.set(errorKey, errorCount + 1, CommonConstants.ACCOUNT_LOCK_ERROR_LOCK_TIME);
            }
            throw new BadCredentialsException("password error");
        }
        //当错误次数不超过限定次数时,登录成功清除错误次数,重新计数
        if (errorCount > 0) {
            redisUtils.del(errorCountKey);
        }
        if (FlymeUtils.isNotEmpty(companyId)) {
            Map<String, Object> attrs = new HashMap<>();
            SysCompany company = companyService.getById(companyId);
            attrs.put("companyName", company.getCompanyName());
            attrs.put("companyCode", company.getCompanyCode());
            Long organizationId = company.getOrganizationId();
            userInfo.setAttrs(attrs);
            userInfo.setOrganizationId(organizationId);
        }
        if (FlymeUtils.isNotEmpty(companyId)) {
            userInfo.setCompanyId(Long.parseLong(companyId));
        }
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }

    @Override
    public boolean supports(Class<?> authentication) {
//      这里直接改成retrun true;表示是支持这个执行
        return true;
    }
}
